using System;
using System.Buffers;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
using UMC.Net;

namespace UMC.Host
{
    abstract class WebSocket : UMC.Net.MimeRequest, IDisposable
    {
        public override bool IsWebSocket => true;
        public abstract void Dispose();



        internal static string GetSecWebSocketAcceptString(string secWebSocketKey)
        {
            string s = secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
            byte[] bytes = Encoding.UTF8.GetBytes(s);
            byte[] inArray = System.Security.Cryptography.SHA1.HashData(bytes);
            return Convert.ToBase64String(inArray);
        }

        internal static bool ProcessWebSocketProtocolHeader(string clientSecWebSocketProtocol, out string acceptProtocol)
        {
            acceptProtocol = string.Empty;
            if (string.IsNullOrEmpty(clientSecWebSocketProtocol))
            {
                return false;
            }
            string[] array = clientSecWebSocketProtocol.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
            if (array.Length > 0)
            {
                acceptProtocol = array[0];
            }
            return true;
        }

        internal static bool ValidateWebSocketHeaders(NetContext context)
        {
            string text = context.Headers["Sec-WebSocket-Version"];
            if (string.IsNullOrEmpty(text))
            {
                return false;
            }
            if (!string.Equals(text, "13", StringComparison.OrdinalIgnoreCase))
            {
                return false;
            }
            string text2 = context.Headers["Sec-WebSocket-Key"];
            if (!string.IsNullOrWhiteSpace(text2))
            {
                try
                {
                    return Convert.FromBase64String(text2).Length == 16;
                }
                catch
                {
                    return false;
                }
            }
            return false;
        }

        internal static void AcceptWebSocketAsyncCore(NetContext context, Stream stream)
        {
            if (context.Url.AbsolutePath == "/UMC.WS")
            {
                var Device = context.Cookies["device"] ?? context.QueryString.Get("device");
                if (String.IsNullOrEmpty(Device) == false)
                {
                    if (ValidateWebSocketHeaders(context) && false)
                    {
                        string secWebSocketKey = context.Headers["Sec-WebSocket-Key"];
                        string secWebSocketAcceptString = GetSecWebSocketAcceptString(secWebSocketKey);
                        var writer = new Net.TextWriter(stream.Write);
                        writer.Write($"HTTP/1.1 101 {HttpStatusDescription.Get(101)}\r\n");
                        writer.Write("Connection: Upgrade\r\n");
                        writer.Write("Upgrade: websocket\r\n");


                        writer.Write($"Sec-WebSocket-Accept: {secWebSocketAcceptString}\r\n");
                        writer.Write($"Sec-WebSocket-Protocol: mqtt\r\n");

                        writer.Write("Server: Apiumc\r\n\r\n");

                        writer.Flush();
                        writer.Dispose();
                        var DeviceId = UMC.Data.Utility.Guid(Device, true).Value;
                        //16384,
                        // context.Tag = WebSocket.CreateFromStream(stream, isServer: true, "mqtt", WebSocket.DefaultKeepAliveInterval);
                        return;
                    }
                }
            }
            context.Error(new ArgumentException("WebSocket"));


        }

    }


    class HttpWebSocket : WebSocket
    {

        public byte[] buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(0x600);
        Socket socket;
        public HttpWebSocket(HttpMime mime, Socket stream)
        {
            this.mime = mime;
            this.socket = stream;
            SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs();
            eventArgs.SetBuffer(buffer);
            eventArgs.Completed += Http;
            if (!this.socket.ReceiveAsync(eventArgs))
            {
                ProcessReceive(eventArgs);
            }
        }
        HttpMime mime;

        private void Http(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
            }
        }
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                try
                {
                    this.mime.Write(buffer, e.Offset, e.BytesTransferred);
                    if (!socket.ReceiveAsync(e))
                    {
                        ProcessReceive(e);
                    }
                }
                catch
                {
                    this.Dispose();
                }
            }
            else
            {
                this.Dispose();

            }
        }

        public override void Receive(byte[] buffer, int offset, int size)
        {
            socket.Send(buffer, offset, size, SocketFlags.None);
        }
        protected override void Header(byte[] data, int offset, int size)
        {

        }
        public override void Dispose()
        {
            this.Dispose();
            try
            {
                socket.Shutdown(SocketShutdown.Both);
            }
            catch
            {

            }
            socket.Close();
            System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
            buffer = null;
        }
    }

}

